<?php
/**
 * Zero_Url_Pattern
 * 
 * Helper Class
 * You should not use this class directly
 * $rules = array('ctl'=>'[a-z_]+', 'act'=>'[a-z_]+', 'action'=>'[a-z_]+', 'tag_id'=>'[\x{4e00}-\x{9fa5}A-Za-z_\-0-9]+'); //tag_id中文
 * 
 * @category   Framework
 * @package    FriendUrl
 * @version    1.0
 * @todo       
 */
class Zero_Url_Pattern
{
    const ATTRIBUTE_SEPARATOR = '/';
    const ATTRIBUTE_SEPARATOR_RULE = '\\/';
    const NAMED_URL_ATTR_IDR = ':';

    private $_patterns = array();
    private $_defaultRule = '[^\/]+';
    private $_pattern = '';
    private $_defaults = array();

    public function __construct($pattern, $rules = array(), $defaults = array())
    {
        $subPatterns = explode(self::ATTRIBUTE_SEPARATOR, $pattern);
        foreach ($subPatterns as $pos=>$subPatternName)
        {
            if($subPatternName{0} == self::NAMED_URL_ATTR_IDR)
            {
                $subPatternName = substr($subPatternName, 1);
                $this->_patterns[$pos] = array('name'=>$subPatternName);
                $this->_patterns[$pos]['rule'] = (!isset($rules[$subPatternName]) || empty($rules[$subPatternName])) ? $this->_defaultRule : $rules[$subPatternName];
                if(isset($defaults[$subPatternName]))
                {
                    $this->_patterns[$pos]['default'] = true;
                }
                else
                {
                    $this->_patterns[$pos]['default'] = false;
                }
            }
            else
            {
                $this->_patterns[$pos] = array('name'=>null, 'rule'=>$subPatternName);
                $this->_patterns[$pos]['default'] = false;
                if($subPatternName == '*')
                {
                    $this->_patterns[$pos]['rule'] = '.*';
                }
            }
        }
        $this->_pattern = $this->getPattern($this->_patterns);
        $this->_defaults = $defaults;
    }

    private function getPattern(&$subPatterns)
    {
        $pattern = '';
        foreach ($subPatterns as $subPattern)
        {
            $pattern = $pattern . '([' . self::ATTRIBUTE_SEPARATOR_RULE  . ']?' . $subPattern['rule'] . ')';
            if(!($subPattern['default'] === false))
            {
                $pattern = $pattern . '?';
            }
        }
        $pattern = '/^' . $pattern . '$/i';
        return $pattern;
    }

    private function getOptionalAttributes($subject)
    {
        $attributes = array();
        $subject = ltrim($subject, self::ATTRIBUTE_SEPARATOR);
        if(empty($subject))
        {
            return $attributes;
        }
        $parts = explode(self::ATTRIBUTE_SEPARATOR, $subject);
        $name = array_shift($parts);
        while(!($name === null))
        {
            $value = array_shift($parts);
            $attributes[$name] = $value;
            $name = array_shift($parts);
        }
        return $attributes;
    }

    public function match($subject, &$totalMatches)
    {
        $subject = rtrim($subject, self::ATTRIBUTE_SEPARATOR);
        $attributes = array();
        $totalMatches = 0;
        if(empty($this->_pattern))
        {
            return false;
        }
        if(preg_match($this->_pattern, $subject, $matches))
        {
            foreach ($this->_patterns as $pos=>$subPattern)
            {
                if(isset($matches[$pos+1]))
                {
                    if($matches[$pos+1] == null)
                    {
                        //sub pattern did not match
                        continue;
                    }
                    else if ($subPattern['name']===null)
                    {
                        ++$totalMatches;
                    }
                    else if(!($subPattern['name']===null))
                    {
                        ++$totalMatches;
                        $attributes[$subPattern['name']] = ltrim($matches[$pos+1], self::ATTRIBUTE_SEPARATOR);
                    }
                    if($subPattern['rule']=='.*')
                    {
                        //should not inc $totalMatches
                        --$totalMatches;
                        //$attributes = array_merge($attributes, $this->getOptionalAttributes($matches[$pos+1]));
                        $attributes = $attributes + $this->getOptionalAttributes($matches[$pos+1]);
                        break;
                    }
                }
            }
            foreach ($this->_defaults as $name=>$value)
            {
                if(!isset($attributes[$name]))
                {
                    $attributes[$name] = $this->_defaults[$name];
                }
            }
        }
        else
        {
            return false;
        }
        return $attributes;
    }

    public function makeUrl($data)
    {
        function test_alter(&$item1, &$key)
        {
           $item1 = "$key/$item1";
        }

        $pvalues = array();

        foreach ($this->_patterns as $subPattern)
        {
            $pos = $subPattern['name'];

            if($subPattern['rule'] == '.*')
            {
                if(isset($data['*']) && is_array($data['*']))
                {
                    ksort($data['*']);
                    //$tmp = array_merge(array_keys($data['*']), $data['*']);
                    $tmp = $data['*'];
                    array_walk($tmp, 'test_alter');

                    $pvalues = array_merge($pvalues, $tmp);
                }

                break;
            }
            else if ($pos === null)
            {
                $pvalues[] = $subPattern['rule'];
            }
            else if(isset($data[$pos]) && !empty($data[$pos]))
            {
                $pvalues[] = $data[$pos];
            }
            elseif (isset($this->_defaults[$pos]))
            {
                $pvalues[] = $this->_defaults[$pos];
            }
        }

        $url = implode('/', $pvalues);

        return $url;
    }
}
?>